- First Part – The basics: the basic principles of how container virtualization is implemented in Windows Server 2016 operating system.
- Second part – Up and Running: creating and managing Windows Server Containers using Docker.
- Third part – Into your Stride Working with Windows Containers and Docker
- Fourth part -- Save the Container Data
In this article I’ll be explaining some of the differences between Windows containers and Docker. To allow us to do more with Windows Containers and Docker, I’ll need to explain a couple of new techniques such as using Docker-Compose to build a multi-container application, and using Image2Docker to port existing Windows application workloads from virtual machines to Docker images. I’ll then go on to explain and demonstrate Hyper-V Isolation because it affects whether you can run a Hyper-V container on Windows Server.
Linux Containers on Windows: Bridging the Gap
Before going further with this article, I must demonstrate that if I try to run a Linux Container on my Windows Container Host, the attempt will fail because the Windows and Linux kernels are fundamentally different. We can’t currently run Linux-based containers on a Windows container host:
1 2 3 4 |
PS > docker pull wordpress Using default tag: latest latest: Pulling from library/wordpress image operating system "linux" cannot be used on this platform |
This problem is being solved, and developers will soon be able to run Linux containers natively on Windows Server using the Hyper-V container isolation technology. Microsoft announced this fact during DockerCon 2017 conference, taking place in Austin, Texas. When this is released, it will remove the need for separate infrastructures and development tools for the two operating systems. At the same DockerCon event, The Docker team announced LinuxKit, a secure and portable Linux subsystem for the container movement. LinuxKit will provide the tooling that will allow us to build custom Linux subsystems that include just the components that are required by the runtime platform. The project to enable this new capability was officially launched at the event. Docker will be working with Microsoft to integrate the LinuxKit subsystem with Hyper-V isolation.
Docker-Compose
Although Docker provides us with a container platform that allows simple and fast deployment, the process of setting up a new environment can be time-consuming, especially if you have more than one service to deploy. Docker-Compose simplifies the installation process to a single deployment command. Docker-Compose is a tool that greatly reduces the time and effort required to define and run multi-container Docker applications. With Docker-Compose, you use a special docker-compose.yml file to configure your application’s services. Then, just by using a single command, you can use data in the file to create and start all the services from your configuration.
There are two steps to Using Docker-Compose:
- Define the services that make up your app in docker-compose.yml to be run together in an isolated environment
- Run docker-compose to run your entire app
You must install the Docker-Compose executable using this command:
1 |
PS > Invoke-WebRequest https://dl.bintray.com/docker-compose/master/docker-compose-Windows-x86_64.exe -UseBasicParsing -OutFile $env:ProgramFiles\docker\docker-compose.exe |
If you already have a Docker-Compose.yml file, then you just have to run the following:
1 |
PS > docker-compose -f docker-compose.yml up |
That’s it. Your App is deployed!
What’s happening here?
When I run the Docker-Compose command, Docker will build a Windows container from the Docker-Compose.yml file. Instead of using one or more dockerfile(s), here I can deploy, for example, an entire application based on a web server and a database. Below is an example of a Docker-Compose.yml file example, written in YAML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
version: '3' services: db: image: microsoft/mssql-server-windows-express environment: sa_password: "Password" ports: - "1433:1433" web: image: getcmd/MyIIS environment: - "Data:DefaultConnection:ConnectionString=Server=db,1433;Database=MyIISWebSite;User Id=sa;Password=Password;MultipleActiveResultSets=True" depends_on: - "db" ports: - "5000:5000" networks: default: external: name: nat |
The “Services:” section will define two services: “db” and “web”. The “db” service is a Microsoft SQL Express image from Microsoft. This service includes some parameters:
- The password for the SA account is set to “Password1”
- Port 1433 on the host is mapped to the exposed port 1433 in the container
Next, the second service named “Web” is built from my custom repository and especially from my custom IIS image which contains a custom website. Then we use an environment variable which defines where the database is, and how to connect to it. Finally, port 5000 is mapped to the exposed port 5000 in the container. The two services are added to an existing network, named nat.
Docker Compose is a particularly good way of managing multi-containers that contain databases and web frontends.
Image2Docker
It is difficult to migrate apps out of Virtual Machines, especially distributed apps with multiple components. Image2Docker may be the simplest way of getting your older applications working on newer operating systems.
What is Image2Docker?
Image2Docker is a PowerShell module that ports existing Windows application workloads from virtual machines to Docker images. It supports multiple application types, but the initial focus is on IIS. You can use Image2Docker to extract ASP.NET websites from a VM, so you can then run them in a Docker container with no application changes. You will need Windows Server 2016 or Windows 10 in order to use Image2Docker.
How does it work?
Image2Docker first inspects the artifacts in a Windows Server 2003, 2008, 2012 or 2016 VM image – in WIM, VHD or VHDX format. It then extracts either an entire VM or specific artifacts from a VHD file. Next, it will generate a Dockerfile which you can build into a Docker image. This PowerShell module requires PowerShell 5.0, or later.
I will now describe the steps that are needed to extract IIS artifacts. In the screenshot below, I deployed a Windows Server 2016 Virtual Machine with the IIS role installed named “IIS01”. This VM has two IIS websites:
First, install the Image2Docker PowerShell module on your container host:
1 2 3 4 5 6 |
PS > Install-Module Image2Docker Untrusted repository You are installing the modules from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from 'PSGallery'? [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"): Y |
Next, you can use the ConvertTo-DockerFile cmdlet. This cmdlet will scan your source image (e.g. VHDX or WIM file) to determine the artifact. Image2Docker currently supports discovery of the following artifacts:
1 2 3 4 5 6 7 8 9 |
PS > Get-WindowsArtifact AddRemovePrograms AllWindowsFeatures Apache DHCPServer DNSServer IIS MSMQ SQLServer |
To scan an image, you just need to call the ConvertTo-Dockerfile cmdlet and specify the -ImagePath parameter which contains the VHDX file. The output folder will contain the generated dockerfile. Before scanning your image, you must power-off the virtual machine.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
PS > Import-Module Image2Docker PS > ConvertTo-Dockerfile -ImagePath "C:\Hyper-V\IIS01\Virtual Hard Disks\IISV01.vhdx" -OutputPath "C:\Containers" -Artifact IIS -Verbose VERBOSE: Reading image file: C:\Hyper-V\IIS01\Virtual Hard Disks\IIS01.vhdx VERBOSE: Image file appears to be a valid WIM or VHDX file. VERBOSE: Image file C:\Hyper-V\IIS01\Virtual Hard Disks\IIS01.vhdx contains 1 images VERBOSE: This image appears to be a valid Virtual Hard Drive (VHDX) file. VERBOSE: Image type is: VHDX VERBOSE: User didn't specify a mount path. Using: C:\Users\Nicolas\AppData\Local\Temp\94decd22-a9aa-45d7-9402-a309c2fa39b0-mount VERBOSE: Finished mounting image C:\Hyper-V\IIS01\Virtual Hard Disks\IIS01.vhdx at mount point C:\Users\Nicolas\AppData\Local\Temp\94decd22-a9aa-45d7-9402-a309c2fa39b0-mount VERBOSE: Finished mounting image to: C:\Users\Nicolas\AppData\Local\Temp\94decd22-a9aa-45d7-9402-a309c2fa39b0-mount VERBOSE: Starting conversion process VERBOSE: Started discovering IIS artifact VERBOSE: Checking IIS ApplicationHost config for Windows Version: 10.0 VERBOSE: Target Image Version 10.0.14393.0 VERBOSE: IIS service is present on the system VERBOSE: ASP.NET is NOT present on the system VERBOSE: .NET 3.5 is NOT present on the system VERBOSE: Finished discovering IIS artifact VERBOSE: Generating Dockerfile based on discovered artifacts in :C:\Users\Nicolas\AppData\Local\Temp\94decd22-a9aa-45d7-9402-a309c2fa39b0-mount VERBOSE: Generating result for IIS component VERBOSE: Copying IIS configuration files VERBOSE: Writing instruction to create site Default Web Site VERBOSE: Processing source directory: C:\inetpub\wwwroot VERBOSE: Writing instruction to expose port for site Default Web Site VERBOSE: Writing instruction to create site GET-CMD VERBOSE: Processing source directory: C:\GET-CMD VERBOSE: Writing instruction to expose port for site GET-CMD VERBOSE: Finished generating the Dockerfile VERBOSE: Finished dismounting the Windows image from C:\Users\Nicolas\AppData\Local\Temp\94decd22-a9aa-45d7-9402-a309c2fa39b0-mount |
You can also extract a single website from an IIS virtual machine using the –ArtifactParam parameter followed by the IIS website name:
1 |
PS > ConvertTo-Dockerfile -ImagePath "C:\Hyper-V\IIS01\Virtual Hard Disks\IIS01.vhdx" -OutputPath "C:\Containers" -Artifact IIS -ArtifactParam "GET-CMD" –Verbose |
Now, you can go to the output folder, and you’ll notice that a Dockerfile has been created.
If you only have a VMDK file, you can use the Microsoft Virtual Machine Converter Tool to convert VMDK images to VHD images.
Hyper-V Isolation
When you are running on Windows 10, you can only work with Hyper-V containers, but when you are running on Windows Server 2016, you can choose between Hyper-V and Windows Server containers. By default, when you use the docker run command, it will start a Windows Server container, but you can specify that you want to run a Hyper-V container by using the —isolation=hyperv parameter.
Before running a Hyper-V container, you must install the Hyper-V role on your container host:
1 |
PS > Install-WindowsFeature -Name Hyper-V |
When running the previous command, you will probably get the following error message if your container host is a virtual machine:
1 |
Hyper-V cannot be installed: The processor does not have required virtualization capabilities |
It means that Nested Virtualization is not enabled on the system. Nested Virtualization allows you to run a Hypervisor inside a Virtual Machine running on a Hypervisor. To enable Nested Virtualization in Hyper-V, you must first shutdown the Container Host Virtual Machine and then run the following PowerShell command:
1 |
PS > Set-VMProcessor -VMName "VMName" -ExposeVirtualizationExtensions $true |
This feature is currently Intel-only: Intel VT-x is required. Once the Hyper-V role is installed, you can run your first Hyper-V container using the following script on your container host:
1 |
PS > docker run --rm -it --name MyNanoHYPV --isolation=hyperv microsoft/nanoserver:latest |
In this example, I deploy a Hyper-V container named “MyNanoHYPV” with the —isolation=hyperv parameter which is based on the Nano Server image from Microsoft.
1 2 3 4 5 6 7 8 9 10 11 12 |
Microsoft Windows [Version 10.0.14393] (c) 2016 Microsoft Corporation. All rights reserved. C:\>ipconfig Windows IP Configuration Ethernet adapter Ethernet: Connection-specific DNS Suffix . : Link-local IPv6 Address . . . . . : fe80::2890:b928:39d6:c8a%4 IPv4 Address. . . . . . . . . . . : 172.21.158.25 Subnet Mask . . . . . . . . . . . : 255.255.240.0 Default Gateway . . . . . . . . . : 172.21.144.1 C:\>hostname b97558fd884a |
As you can see, the Hyper-V container boots in seconds; much faster than a virtual machine. To confirm that you are running a Hyper-V container, you can run a very simple check. Open the PowerShell console, and use the Get-Process cmdlet to list the process named “VMWP” which corresponds to the Hyper-V process:
1 2 3 4 5 |
PS > Get-Process -Name vmwp Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName ------- ------ ----- ----- ------ -- -- ----------- 3386 18 38796 20048 4.39 3632 0 vmwp 235 14 35888 16128 2.63 5224 0 vmwp |
We can see from this result that there are two processes running on the container host. Now, just type the “exit” command inside your Hyper-V container and rerun the previous command:
1 2 3 4 5 |
PS C:\Users\Administrator> Get-Process -Name vmwp Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName ------- ------ ----- ----- ------ -- -- ----------- 235 14 35888 16128 2.63 5224 0 vmwp PS C:\Users\Administrator> |
There is only one process. Why? Hyper-V container is like a virtual machine in some ways but different in others. When you run your Hyper-V container, Windows will create what seems to be a VM but it’s not actually a virtual machine, it’s a Hyper-V container! Of course, you can’t see this VM in the Hyper-V console manager. The only thing that you can see is the process. The use of a Hyper-V container provides a kernel-mode isolation instead of user-mode isolation.
Ok, now let’s examine another example to understand the difference between Windows Server containers and Hyper-V containers. Run the following command:
1 |
PS > docker run -d --name NanoWithoutHYPV microsoft/nanoserver:latest ping localhost –t |
This command deploys a Windows Server container based on the Nano Server image. We run a permanent ping inside the container. Now, use the Docker Top command to display the list of the process running inside this container:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
PS > docker top NanoWithoutHYPV Name PID CPU Private Working Set smss.exe 5384 00:00:00.140 208.9kB csrss.exe 1920 00:00:00.437 356.4kB wininit.exe 3320 00:00:00.031 651.3kB services.exe 5780 00:00:00.203 1.487MB lsass.exe 5752 00:00:00.234 1.95MB svchost.exe 2800 00:00:00.062 1.241MB svchost.exe 2856 00:00:00.031 1.323MB svchost.exe 5852 00:00:00.125 2.179MB svchost.exe 5800 00:00:00.000 1.434MB svchost.exe 4544 00:00:00.140 3.752MB svchost.exe 4412 00:00:00.156 1.942MB svchost.exe 4844 00:00:00.062 1.47MB svchost.exe 5876 00:00:00.328 4.039MB svchost.exe 3016 00:00:00.109 2.327MB CExecSvc.exe 3136 00:00:00.000 737.3kB PING.EXE 3912 00:00:00.000 536.6kB PS C:\Users\Administrator> get-process *PING* Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName ------- ------ ----- ----- ------ -- -- ----------- 62 5 720 3480 0.00 3912 3 PING |
We can see that the last process, called “PING.EXE”, corresponds to the ping command inside the container. But, running the Get-Process cmdlet on the container host, notice that the same process exists with the same PID! It means that our Windows Server container uses the Kernel resources from my container host. OK, now stop this container with the Docker Stop command and do the same thing by appending the —isolation=hyperv parameter:
1 |
PS > docker run -d --name NanoWithHYPV --isolation=hyperv microsoft/nanoserver:latest ping localhost –t |
And run the Docker Top command:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
PS > docker top NanoWithHYPV Name PID CPU Private Working Set smss.exe 828 00:00:00.312 213kB csrss.exe 848 00:00:00.703 356.4kB wininit.exe 888 00:00:00.140 618.5kB services.exe 900 00:00:00.453 1.384MB lsass.exe 916 00:00:00.406 1.933MB svchost.exe 1008 00:00:00.109 1.237MB svchost.exe 216 00:00:00.125 1.245MB svchost.exe 444 00:00:00.187 1.184MB svchost.exe 724 00:00:00.062 1.073MB svchost.exe 984 00:00:00.375 3.895MB svchost.exe 712 00:00:00.265 1.88MB svchost.exe 1028 00:00:00.078 1.528MB svchost.exe 1056 00:00:00.296 3.441MB svchost.exe 1168 00:00:00.390 2.273MB CExecSvc.exe 1188 00:00:00.031 737.3kB PING.EXE 1396 00:00:00.046 532.5kB PS C:\Users\Administrator> get-process *PING* get-process : Cannot find a process with the name "ping". Verify the process name and call the cmdlet again. At line:1 char:1 + get-process -Name ping + ~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : ObjectNotFound: (ping:String) [Get-Process], ProcessCommandException + FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand |
PowerShell error! No PING process exists on the container host. It means that due to the Hyper-V isolation and especially the kernel mode isolation, the kernel resources are not shared between the container host and the Hyper-V containers.
For those of you who are getting the following error:
1 2 3 |
PS C:\Users\Administrator> docker run --rm -it --name nanohyperv --isolation=hyperv microsoft/nanoserver:latest C:\Program Files\Docker\docker.exe: Error response from daemon: container 2f83bfc2bbbbed5a0425a0219a7a747c971a1eeb032ce11b76149f17b397f17b en countered an error during CreateContainer: failure in a Windows system call: No hypervisor is present on this system. (0xc0351000) |
It means that Hyper-V role is not installed on your system, so you can’t run Hyper-V containers. If you have some trouble using a specific container, you can use the Docker Logs command to troubleshoot. The Docker logs command shows information logged by a running container. The information that is logged and the format of the log depends almost entirely on the container’s endpoint command:
1 |
PS > docker logs NanoWithHYPV |
Conclusion
In this part, we discussed about Docker Compose. To build a multi-container application, Docker has developed Docker-Compose which makes it easier to configure and run applications made up of multiple containers. Docker-Compose starts all the required containers with a single command.
Next, we used Image2Docker that allows you to take a virtualized web server in a Hyper-V VM and extract a Docker image for each website in the Virtual Machine. It looks at the disk for known artifacts, compiles a list of all the artifacts installed on the VM and generates a Dockerfile to package the artifacts.
Finally, we described the Hyper-V container concept. Hyper-V containers provide a kernel mode isolation instead of user mode isolation. Hyper-V containers use an automatically generated Hyper-V VM where the container instances run.
Load comments